
import json
import os
import time
from coin.flow.topic_record import TopicRecordWriter
from coin2.service.order_detector.common import (QuerySpec, OrderContext, RAW_INFO_SAVE_ROOT_PATH, OpenOrderElement,
                                                 ABNORMAL_OPEN_SAVE_ROOT_PATH, ABNORMAL_CLOSE_SAVE_ROOT_PATH, ABNORMAL_OPEN_ORDER_STORAGE_REFRESH_TIME)
from coin2.service.order_detector.influxdb import (InfluxdbOutPut)
import coin2.service.order_detector.common as common
import copy

import datetime


class TopicRecordStorage(object):
    def __init__(self, exchange: str, root_path=RAW_INFO_SAVE_ROOT_PATH, auto_update_file=True):
        self._root_path = root_path
        self._exchange = exchange
        self._topic_writer = None
        self._minute = datetime.datetime.utcnow().minute
        self._hour = datetime.datetime.utcnow().hour
        self._auto_update_file = auto_update_file

    def __update_file_path(self):
        if self._topic_writer is not None:
            self._topic_writer.close()

        date = datetime.datetime.utcnow()
        date_dir_str = date.strftime("%Y%m%d")
        date_file_str = date.strftime("%Y-%m-%d_%H:%M:%S")

        # ~/workspace/detectro_log/20221214/Binance/
        dir_path = self._root_path + '/' + date_dir_str  + '/' + self._exchange + '/'
        os.makedirs(dir_path, exist_ok=True)
        filename = f'{dir_path}/{self._exchange}_{date_file_str}.gz'
        self._topic_writer = TopicRecordWriter(filename)
        self._hour = date.hour

    def write(self, document):
        timestamp = time.time_ns()
        minute = datetime.datetime.utcnow().minute
        hour = datetime.datetime.utcnow().hour

        if self._topic_writer == None:
            self.__update_file_path()

        if self._auto_update_file and hour != self._hour:
            self.__update_file_path()

        self._topic_writer.write(None,  # queue seq
                                  0,  # topic id
                                  None,  # topic seq
                                  timestamp,
                                  json.dumps(document))

        if self._minute != minute:
            self._minute = minute
            self._topic_writer.flush()

    def close(self):
        self._topic_writer.close()


class AbnormalOpenOrderStorage():
    def __init__(self, exchange: str, root_path=ABNORMAL_OPEN_SAVE_ROOT_PATH, refresh_time=ABNORMAL_OPEN_ORDER_STORAGE_REFRESH_TIME) -> None:
        self._root_path = root_path
        self._exchange = exchange
        self._cache_map = {}
        self._spec_map = {}
        self._refresh_time = refresh_time
        self._record_time = time.time_ns()
        os.makedirs(self._root_path, exist_ok=True)
    
    @staticmethod
    def load_abnormal_open_order(exchange)->list:
        path = AbnormalOpenOrderStorage.get_abnormal_save_path(exchange)
        try:
          with open(path, "r") as f:
            record = json.loads(f)
        except Exception as e:
          print("ignore ", path, e)
          return {}

        info = record["info"]
        strategy_ele_map = {}
        for item in info:
          strategy_name = item["spec"]["strategy_name"]
          ele_list = []
          for ord in item["order"]:
            ele = OpenOrderElement(ord["order_id"], ord["order_info"], "fetch_time")
            ele_list.append(ele)
          strategy_ele_map[strategy_name] = ele_list

        return strategy_ele_map

    @staticmethod
    def get_abnormal_save_path(exchange: str, root_path = ABNORMAL_OPEN_SAVE_ROOT_PATH):
        path = root_path + "/" + exchange + "_abnormal_open.json"
        return path

    def __refresh(self):
        dump_info = {"refresh_time": datetime.datetime.utcnow().strftime(
            "%Y-%m-%d_%H:%M:%S"), "info": []}
        for strategy_name, oc_list in self._cache_map.items():
            strategy_dump_info = {"spec": self._spec_map[strategy_name], "order": []}
            for oc in oc_list:
                assert (isinstance(oc, OrderContext))
                order_info = oc.get_order_summary()
                strategy_dump_info["order"].append(order_info)

            dump_info["info"].append(strategy_dump_info)

        save_path = AbnormalOpenOrderStorage.get_abnormal_save_path(self._exchange, self._root_path)
        with open(save_path, "w+") as f:
            json.dump(dump_info, f, indent=2)

    def write(self, spec: QuerySpec, oc_list: list):
        if len(oc_list) == 0:
            return 

        for oc in oc_list:
            assert (isinstance(oc, OrderContext))

        self._cache_map[spec.strategy_name] = copy.deepcopy(oc_list)
        self._spec_map[spec.strategy_name] = copy.deepcopy(spec.spce())
        if time.time_ns() - self._record_time > self._refresh_time:
            self.__refresh()

    def close(self):
        self.__refresh()
        self._cache_map = {}
        self._spec_map = {}

class AbnormalCloseOrderStorage():
    def __init__(self, exchange: str, root_path=ABNORMAL_CLOSE_SAVE_ROOT_PATH) -> None:
        self._root_path = root_path
        self._exchange = exchange
        self._write_fd = None
        self._save_dir = AbnormalCloseOrderStorage.get_abnormal_save_dir(root_path)

    @staticmethod
    def get_abnormal_save_dir(root_path=ABNORMAL_CLOSE_SAVE_ROOT_PATH):
        date = datetime.datetime.utcnow().strftime("%Y%m%d")
        path = root_path + "/" + date + "/"
        return path

    def __write(self, info_str: str):
        save_dir = AbnormalCloseOrderStorage.get_abnormal_save_dir(self._root_path)
        os.makedirs(save_dir, exist_ok=True)
        if save_dir != self._save_dir or self._write_fd is None:
            self._save_dir = save_dir
            os.makedirs(save_dir, exist_ok=True)
            file_path = save_dir + self._exchange + "_abnormal_close.json"
            self._write_fd = open(file_path, "a+")

        self._write_fd.write(info_str)
        self._write_fd.write("\n")
        self._write_fd.flush()

    def write(self, spec: QuerySpec, oc_list: list):
        if len(oc_list) == 0:
            return

        spe = spec.spce()
        for oc in oc_list:
            assert (isinstance(oc, OrderContext))
            order_info = oc.get_order_summary()
            dump_info = json.dumps({**order_info, **spe})
            self.__write(dump_info)

    def close(self):
        self._write_fd.flush()
        self._write_fd.close()


class InfluxdbOrderStorage():
    def __init__(self) -> None:
        self._token = common.influxdb_token
        self._org = common.influxdb_org
        self._bucket = common.influxdb_bucket
        self._url = common.influxdb_url
        self._influx_client = InfluxdbOutPut(
            url=self._url, token=self._token, org=self._org, bucket=self._bucket)

    def __get_tag_by_spec(spec: QuerySpec) -> dict:
        tag = {}
        tag["mea"] = spec.mea
        tag["strategy_name"] = spec.strategy_name
        return tag

    def write_by_ele_list(self, spec: QuerySpec, ele_list: list, ts: int):
        open_order_size = len(ele_list)
        open_order = {"num_of_open_orders": open_order_size}
        tag = InfluxdbOrderStorage.__get_tag_by_spec(spec)
        self._influx_client.write_data_spot("num_of_open_order", tag, open_order, ts)

    def write_by_abnormal_open_oc(self, spec: QuerySpec, oc_list: list, ts: int):
        tag = InfluxdbOrderStorage.__get_tag_by_spec(spec)

        for oc in oc_list:
            filed = {"stay_time_sec": oc.stay_time_sec, "order_id": oc.order_id}
            self._influx_client.write_data_spot("abnormal_info", tag, filed, ts)

    def close(self):
      self._influx_client.close()